// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package ssa

import "cmd/internal/src"

// trim删除块中没有代码的块。
// 插入这些块是为了删除关键边。
func trim(f *Func) {
	n := 0
	for _, b := range f.Blocks {
		if !trimmableBlock(b) {
			f.Blocks[n] = b
			n++
			continue
		}

		bPos := b.Pos
		bIsStmt := bPos.IsStmt() == src.PosIsStmt

		// 将b从图中拼接出来。注：`mergePhi`取决于
		// 顺序，在此合并前置边。
		p, i := b.Preds[0].b, b.Preds[0].i
		s, j := b.Succs[0].b, b.Succs[0].i
		ns := len(s.Preds)
		p.Succs[i] = Edge{s, j}
		s.Preds[j] = Edge{p, i}

		for _, e := range b.Preds[1:] {
			p, i := e.b, e.i
			p.Succs[i] = Edge{s, len(s.Preds)}
			s.Preds = append(s.Preds, Edge{p, i})
		}

		// 尝试保留语句边界
		if bIsStmt {
			sawStmt := false
			for _, v := range s.Values {
				if isPoorStatementOp(v.Op) {
					continue
				}
				if v.Pos.SameFileAndLine(bPos) {
					v.Pos = v.Pos.WithIsStmt()
				}
				sawStmt = true
				break
			}
			if !sawStmt && s.Pos.SameFileAndLine(bPos) {
				s.Pos = s.Pos.WithIsStmt()
			}
		}
		// 如果's'有多个前置机，请将其phi ops更新为
		// 合并的帐户。
		if ns > 1 {
			for _, v := range s.Values {
				if v.Op == OpPhi {
					mergePhi(v, j, b)
				}

			}
			// 如果phi ops已合并到“b”中，请将其从“b”中删除
			// 's'的φ运算。
			k := 0
			for _, v := range b.Values {
				if v.Op == OpPhi {
					if v.Uses == 0 {
						v.resetArgs()
						continue
					}
					// 填充剩余phi操作的参数，以便
					// 它们与新的前置计数“s”匹配。
					// 因为s没有对应于
					// φop在b中，其他边进入s
					// 必须是s的环回边，所以v是正确的
					// 对v的论点！
					args := make([]*Value, len(v.Args))
					copy(args, v.Args)
					v.resetArgs()
					for x := 0; x < j; x++ {
						v.AddArg(v)
					}
					v.AddArg(args[0])
					for x := j + 1; x < ns; x++ {
						v.AddArg(v)
					}
					for _, a := range args[1:] {
						v.AddArg(a)
					}
				}
				b.Values[k] = v
				k++
			}
			b.Values = b.Values[:k]
		}

		// 合并块的值。
		for _, v := range b.Values {
			v.Block = s
		}
		k := len(b.Values)
		m := len(s.Values)
		for i := 0; i < k; i++ {
			s.Values = append(s.Values, nil)
		}
		copy(s.Values[k:], s.Values[:m])
		copy(s.Values, b.Values)
	}
	if n < len(f.Blocks) {
		f.invalidateCFG()
		tail := f.Blocks[n:]
		for i := range tail {
			tail[i] = nil
		}
		f.Blocks = f.Blocks[:n]
	}
}

// emptyBlock报告块是否不包含实际值
// 说明书
func emptyBlock(b *Block) bool {
	for _, v := range b.Values {
		if v.Op != OpPhi {
			return false
		}
	}
	return true
}

// trimmableBlock报告是否可以从CFG中修剪块，
// 符合以下标准：
// -它不应该是第一个街区
// -它应该是块平原
// -它不应该回到自身
// -它或者是后继块的单个前置块，或者
// 没有实际的说明
func trimmableBlock(b *Block) bool {
	if b.Kind != BlockPlain || b == b.Func.Entry {
		return false
	}
	s := b.Succs[0].b
	return s != b && (len(s.Preds) == 1 || emptyBlock(b))
}

// mergePhi调整'v'参数的数量以说明合并
// 属于'b'，它是'v'块的第'i'个前身。
func mergePhi(v *Value, i int, b *Block) {
	u := v.Args[i]
	if u.Block == b {
		if u.Op != OpPhi {
			b.Func.Fatalf("value %s is not a phi operation", u.LongString())
		}
		// 如果原始块包含u=φ（u0，u1，…，un）和
		// 目前的phi为
		// v=φ（v0，v1，…，u，…，vk）
		// 然后合并的phi是
		// v=φ（v0，v1，…，u0，…，vk，u1，…，un）
		v.SetArg(i, u.Args[0])
		v.AddArgs(u.Args[1:]...)
	} else {
		// 如果原始块包含u=φ（u0，u1，…，un）和
		// 目前的phi为
		// v=φ（v0，v1，…，vi，…，vk）
		// i、 e.它不使用前置块中的值，
		// 然后合并的phi是
		// v=φ（v0，v1，…，vk，vi，vi，…）
		for j := 1; j < len(b.Preds); j++ {
			v.AddArg(v.Args[i])
		}
	}
}
